import java.util.ArrayList;
import java.util.HashMap;
import java.lang.Math;


import static java.lang.System.out;

public final class ABMiniMaxDrawFixHM extends FixedDepth {

    private final int MAX = WHITE_WIN;
    private final int MIN = WHITE_LOSS;

    final static Integer ZERO = Integer.valueOf(0);
    final static Integer ONE = Integer.valueOf(1);


    private HashMap<Board, Integer> boardHistory;

	//    private Board[] boardHistory = new Board[1000];
	//    private int boardIdx = 0;


    public ABMiniMaxDrawFixHM(boolean isBlack, Eval evaluator, int searchDepth) {
        super(isBlack, evaluator, searchDepth);
	boardHistory = new HashMap<Board, Integer>();
    }




    @Override
    public Board findMove(Board board) {
        // IMPORTANT! If the player using findMove is White, then
        // the search method will be called with BLACK to move and
        // vice versa, i.e. THE RETURN VALUE FROM THE SEARCH-CALL WILL
        // REFLECT THE OPPONENTS BEST POSSIBLE SCORE
        assert board.blackToMove == isBlack;

        int bestValue = MIN;
        if (isBlack) {
            bestValue = MAX;
        }
	int alpha = MIN;
	int beta = MAX;


        ArrayList<Board> moves = board.generateMoves();
        Board bestMove = null;
        if (moves.size() > 0) {
            bestMove = moves.get(0);
        }
        for (Board move : moves) {
            int value = search(move, searchDepth, alpha, beta);
            if (isBlack) {
                if (value < bestValue) {
		    if (value < beta) {
			beta = value;
		    }
                    bestValue = value;
                    bestMove = move;
                }
            } else {
                if (value > bestValue) {
		    if (value > alpha) {
			alpha = value;
		    }
                    bestValue = value;
                    bestMove = move;
                }
            }
        }

        if (bestMove != null) {

	    if(printBestMoveValue) {
		out.println("The best move has value " + bestValue);
	    }

	    int nOldPieces = BitUtils.countBits( board.black | board.white );
	    int nOldKings  = BitUtils.countBits( board.kings );
	    int nNewPieces = BitUtils.countBits( bestMove.black | bestMove.white );
	    int nNewKings  = BitUtils.countBits( bestMove.kings );

	    boolean piecesTaken = (nOldPieces - nNewPieces != 0);
	    boolean wasCrowned  = (nOldKings - nNewKings != 0);

	    if(piecesTaken || wasCrowned) {
		boardHistory.clear();
		out.println("cleared");
	    }
	    Integer occurrences = boardHistory.get(bestMove);
	    if( occurrences != null ) {
		int o = occurrences + 1;
		out.println("Put board in boardHistory with a count of " + o);
		//GameEngine.printBoard(bestMove);
		boardHistory.put(bestMove, o);
	    }
	    else {
		out.println("Put a new board in boardHistory with a count of 1");
		boardHistory.put(bestMove, ONE);
	    }


        }

        return bestMove;
    }

    //    @Override
    public int search(Board board, int depthLeft, int alpha, int beta) {

        ArrayList<Board> moves = board.generateMoves();
        //if unable to move, this is a losing board for current player
	//punish for depth left (embarrassing to lose with much depth left)
        if(moves.isEmpty()) {
            if (board.blackToMove) {
                return BLACK_LOSS+depthLeft;
            } else {
                return WHITE_LOSS-depthLeft;
            }
        }

	
	Integer occurrences = boardHistory.get(board);
	if(occurrences!=null){
	    if ( occurrences.intValue() > 2 && isBlack) {//board.blackToMove ) {
		return DRAW;
	    } else if (occurrences.intValue() > 2 && !isBlack ) { //!board.blackToMove ) 
		return -DRAW;
	    }
	}


        if(depthLeft == 0) {
            return evaluator.eval(board);
        }

        depthLeft--;
	int nodeCount = 0;
        while (  (nodeCount < moves.size())  &&  (alpha < beta)  ) {
	    Board move = moves.get(nodeCount);
	    int value;

	    occurrences = boardHistory.get(move);
	    if(occurrences == null ) {
		boardHistory.put(move, ONE);
		value = search(move, depthLeft, alpha, beta);
		boardHistory.put(move, ZERO);
	    }
	    else {
		boardHistory.put(move,occurrences+1);
		value = search(move, depthLeft, alpha, beta);
		boardHistory.put(move,occurrences);
	    }

            if (board.blackToMove) {
                if (value < beta) {
                    beta = value;
                }   
            } else {
                if (value > alpha) {
                    alpha = value;
                }
            }
	    nodeCount++;
        }

	int bestValue;
        if (board.blackToMove) {
            bestValue = beta;
        } else {
            bestValue = alpha;
        }

        return bestValue;
    }
}
